#pragma once

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <unistd.h>

using namespace std;

const char* pathname = "/home/xyc/code";
const int proj_id = 0x66;
// 在内核中,共享内存的大小是以4KB为基本单位的
// 你只能用你申请的大小(4096),建议申请大小为n*4KB
const int defaultsize = 4096;//单位是字节

key_t GetShmKey() //创建key值
{
    key_t key = ftok(pathname, proj_id);
    if(key < 0)
    {
        cout << "ftok error, errno: " << errno << ", errno string: " << strerror(errno) << endl;
        exit(1);
    }
    return key;
}

string ToHex(key_t key) //转为十六进制
{
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "0x%x", key);
    return buffer;
}

int CreatShmOrDie(key_t key, size_t size, int flag) //创建共享内存
{
    int shmid = shmget(key, size, flag);
    if(shmid < 0)
    {
        cout << "shmget error, errno: " << errno << ", errno string: " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int CraetShm(key_t key, size_t size) //创建共享内存
{
    //IPC_CREAT:不存在就创建,存在就获取
    //IPC_EXCL:没有意义
    //IPC_CREAT | IPC_EXCL:不存在就创建,存在就出错返回
    return CreatShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666);
}
int GetShm(key_t key, size_t size) // 获取共享内存
{
    return CreatShmOrDie(key, size, IPC_CREAT);
}

void DeleteShm(int shmid) //删除共享内存
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    if(n < 0)
    {
        cout << "shmctl error, errno: " << errno << ", errno string: " << strerror(errno) << endl;
    }
    else
    {
        cout << "shmid delete shm success, shmid: " << shmid << endl;
    }
}

void ShmDebug(int shmid) // 获取共享内存的属性
{
    struct shmid_ds shmds;//用户级
    int n = shmctl(shmid, IPC_STAT, &shmds);//内核级
    if(n < 0)
    {
        cout << "shmctl error, errno: " << errno << ", errno string: " << strerror(errno) << endl;
        return;
    }
    cout << "shmds.shm_segsz: " << shmds.shm_segsz << endl;
    cout << "shmds.shm_nattch: " << shmds.shm_nattch << endl;
    cout << "shmds.shm_ctime: " << shmds.shm_ctime << endl;
    cout << "shmds.shm_perm.__key: " << ToHex(shmds.shm_perm.__key) << endl;
}

void* ShmAttach(int shmid)
{
    void* addr = shmat(shmid, nullptr, 0);
    if((long long)addr == -1) // 避免精度损失报错
    {
        cout << "shmat error, errno: " << errno << ", errno string: " << strerror(errno) << endl;
        return nullptr;
    }
    return addr;
}

void ShmDetach(void* addr)
{
    int n = shmdt(addr);
    if(n < 0)
    {
        cout << "shmdt error, errno: " << errno << ", errno string: " << strerror(errno) << endl;
        return;
    }
}